﻿using iTool.ClusterComponent;
using iTool.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace iTool.ClusterComponent
{
    public class LimitWorkScopeProvider : IWorkScopeProvider
    {
        public string WorkScopeName { get; }

        public IClusterService ClusterService { get; }
        private long Limit { get; }

        public LimitWorkScopeProvider(long limit, string workScopeName)
        {
            this.WorkScopeName = workScopeName;
            this.Limit = limit;
            this.ClusterService = iBox.GetService<IClusterService>("IClusterService");
        }

        public async Task<IWorkUnitProvider> CreateWorkUnitScopeAsync()
        {
            var Q = this.GetQueueParameters();
            TaskCompletionSource<Task> source = this.CreateTaskSourceAndCheckStartNexting(Q);
            await source.Task;
            Q.runingQueue.Enqueue(new TaskCompletionSource<Task>());

            bool isTriggerError = false;
            try
            {
                var scope = new LimitWorkUnitProvider(this.Limit, this);
                await scope.ApplyExcuterAsync();
                return scope;
            }
            catch (Exception)
            {
                isTriggerError = true;
                throw;
            }
            finally
            {
                if (isTriggerError)
                {
                    await NextAsync();
                }
            }


        }

        public Task NextAsync()
        {
            if (CurrentRuningQueueDictionary[this.WorkScopeName].TryDequeue(out _))
            {
                if (CurrentRuningQueueDictionary[this.WorkScopeName].Count < this.Limit)
                {
                    lock (LoopTaskSoureDictionary)
                    {
                        LoopTaskSoureDictionary[this.WorkScopeName].NextTask();
                    }
                }
            }

            return Task.CompletedTask;
        }

        private TaskCompletionSource<Task> CreateTaskSourceAndCheckStartNexting((ConcurrentQueue<TaskCompletionSource<Task>> waitQueue, ConcurrentQueue<TaskCompletionSource<Task>> runingQueue) Q)
        {
            TaskCompletionSource<Task> source = new TaskCompletionSource<Task>();
            Q.waitQueue.Enqueue(source);

            if (!LoopTaskSoureDictionary.TryGetValue(this.WorkScopeName, out LoopTaskSoure loopTaskSoure))
            {
                lock (LoopTaskSoureDictionary)
                {
                    if (!LoopTaskSoureDictionary.TryGetValue(this.WorkScopeName, out loopTaskSoure))
                    {
                        loopTaskSoure = new LoopTaskSoure(WaitCreateWorkUnitScopeQueueDictionary[this.WorkScopeName]);
                        LoopTaskSoureDictionary.TryAdd(this.WorkScopeName, loopTaskSoure);
                    }
                }
            }

            if (Q.runingQueue.Count < this.Limit)
            {
                lock (loopTaskSoure)
                {
                    loopTaskSoure.NextTask();
                }
            }

            return source;

        }

        private (ConcurrentQueue<TaskCompletionSource<Task>> waitQueue, ConcurrentQueue<TaskCompletionSource<Task>> runingQueue) GetQueueParameters()
        {
            var waitQueue = WaitCreateWorkUnitScopeQueueDictionary.GetOrAdd(this.WorkScopeName, new ConcurrentQueue<TaskCompletionSource<Task>>());
            var runingQueue = CurrentRuningQueueDictionary.GetOrAdd(this.WorkScopeName, new ConcurrentQueue<TaskCompletionSource<Task>>());
            return (waitQueue, runingQueue);
        }

        #region Static

        static ConcurrentDictionary<string, ConcurrentQueue<TaskCompletionSource<Task>>> CurrentRuningQueueDictionary;
        static ConcurrentDictionary<string, ConcurrentQueue<TaskCompletionSource<Task>>> WaitCreateWorkUnitScopeQueueDictionary;
        static ConcurrentDictionary<string, LoopTaskSoure> LoopTaskSoureDictionary;
        static LimitWorkScopeProvider()
        {
            CurrentRuningQueueDictionary = new ConcurrentDictionary<string, ConcurrentQueue<TaskCompletionSource<Task>>>();
            WaitCreateWorkUnitScopeQueueDictionary = new ConcurrentDictionary<string, ConcurrentQueue<TaskCompletionSource<Task>>>();
            LoopTaskSoureDictionary = new ConcurrentDictionary<string, LoopTaskSoure>();
        }

        #endregion 
    }
}
